<?php
	/**
	 * This file contains the QDataGridBase and QDataGridRow class.
	 *
	 * @package Controls
	 */

	/**
	 * Due to the fact that DataGrid's will perform php eval's on anything that is back-ticked within each column/row's
	 * DataGridColumn::HTML, we need to set up this special DataGridEvalHandleError error handler to correctly report
	 * errors that happen.
	 *
	 * @global string $__exc_dtg_errstr
	 * @param mixed $__exc_errno
	 * @param string $__exc_errstr
	 * @param string $__exc_errfile
	 * @param string $__exc_errline
	 */
	function DataGridEvalHandleError($__exc_errno, $__exc_errstr, $__exc_errfile, $__exc_errline) {
		$__exc_objBacktrace = debug_backtrace();
		for ($__exc_intIndex = 0; $__exc_intIndex < count($__exc_objBacktrace); $__exc_intIndex++) {
			$__exc_objItem = $__exc_objBacktrace[$__exc_intIndex];

			if ((strpos($__exc_errfile, "DataGrid.inc") !== false) &&
				(strpos($__exc_objItem["file"], "DataGrid.inc") === false)) {
				$__exc_errfile = $__exc_objItem["file"];
				$__exc_errline = $__exc_objItem["line"];
			} else if ((strpos($__exc_errfile, "Form.inc") !== false) &&
				(strpos($__exc_objItem["file"], "Form.inc") === false)) {
				$__exc_errfile = $__exc_objItem["file"];
				$__exc_errline = $__exc_objItem["line"];
			}
		}

		global $__exc_dtg_errstr;
		if (isset($__exc_dtg_errstr) && ($__exc_dtg_errstr))
			$__exc_errstr = sprintf("%s\n%s", $__exc_dtg_errstr, $__exc_errstr);
		QcodoHandleError($__exc_errno, $__exc_errstr, $__exc_errfile, $__exc_errline, null);
	}

	/**
	 * @package Controls
	 *
	 * @property-write QDataGridRowStyle $Style
	 */
	class QDataGridRow extends QControl {
		public function GetHtml($strColumnsHtml) {
			$strToReturn = sprintf('<tr id="%s" %s>%s</tr>', $this->strControlId, $this->GetAttributes(), $strColumnsHtml);
			return $strToReturn;
		}
		
		protected function GetControlHtml() { }
		public function ParsePostData() {}
		public function Validate() {}
		
		/////////////////////////
		// Public Properties: SET
		/////////////////////////
		public function __set($strName, $mixValue) {
			switch ($strName) {
				case "Style":
					try {
						$objStyle = QType::Cast($mixValue, "QDataGridRowStyle");
						$this->BackColor = $objStyle->BackColor;
						$this->BorderColor = $objStyle->BorderColor;
						$this->BorderStyle = $objStyle->BorderStyle;
						$this->BorderWidth = $objStyle->BorderWidth;
						$this->CssClass = $objStyle->CssClass;
						$this->FontBold = $objStyle->FontBold;
						$this->FontItalic = $objStyle->FontItalic;
						$this->FontNames = $objStyle->FontNames;
						$this->FontOverline = $objStyle->FontOverline;
						$this->FontSize = $objStyle->FontSize;
						$this->FontStrikeout = $objStyle->FontStrikeout;
						$this->FontUnderline = $objStyle->FontUnderline;
						$this->ForeColor = $objStyle->ForeColor;
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				default:
					try {
						parent::__set($strName, $mixValue);
						break;
					} catch (QCallerException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
			}
		}
	}

	/**
	 * NOTE: Due to the use of "Eval", DataGrid is currently being redone to not use EVAL.
	 * A new datagrid control will be released in Beta 2 of the framework, which will have a much
	 * more secure and robust render handler.  Naturally, generated ListForms will be updated, accordingly,
	 * to work with the new, improved DataGrid class.
	 *
	 * DataGrid control is used to display tabular information (e.g. lists)
	 *
	 * The control itself will display things based off of an array of objects that gets set as the "Data Source".
	 * It is particularly useful when combined with the Class::LoadArrayByXXX() functions or the Class::LoadAll()
	 * that is generated by the CodeGen framework, or when combined with custom Class ArrayLoaders that you define
	 * youself, but who's structure is based off of the CodeGen framework.
	 *
	 * The DataGrid essentially is a <table>. For each item in a datasource's Array, a row (<tr>) will be generated.
	 * You can define any number of DataGridColumns which will result in a <td> for each row.
	 * Within the DataGridColumn, you can specify the DataGridColumn::Html that should be displayed.
	 *
	 * The HTML looks for the special back-tick character "`", and will do PHP Eval's on antyhing within a pair of ` `.
	 * Moreover, the special variable $_ROW can be used to access the actual contents of that particular row's data
	 * in the main data source array.
	 *
	 * So, for example, supposed the following:
	 * 		$strSimpleArray = {"Blah", "Foo", "Baz", "Fun"};
	 * 		$dtgDataGrid = new DataGrid("dtgDataGrid");
	 * 		$dtgDataGrid->AddColumn("Column Heading", "Contents: `$_ROW`");
	 * 		$dtgDataGrid->DataSource = $strSimpleArray;
	 * This will generate a simple 4-row, 1-column table that contains the following:
	 *      Column Heading
	 *      --------------
	 *      Contents: Blah
	 *      Contents: Foo
	 *      Contents: Baz
	 *      Contents: Fun
	 * In this case, $_ROW is a string, itself, which is each item in the DataSource's array.
	 *
	 * Utilizing the back-tick and $_ROW feature, you can do infinitely more complicatd display functionality:
	 * 		$dtgDataGrid = new DataGrid("dtgDataGrid");
	 * 		$dtgDataGrid->AddColumn("Title", "<b>`$_ROW->Title`</b>");
	 * 		$dtgDataGrid->AddColumn("Calculated Result", "`DisplayResults($_ROW->Calculate())`");
	 * 		$dtgDataGrid->DataSource = Foo::LoadAll();
	 * This could then generate a table with much more data-rich information:
	 *      Title             Calculated Result
	 *      ----------------- --------------------
	 *      Some Title Here   $28,298.24
	 *      Foo Baz Bar       $18,000.00
	 *      Blah              (None)
	 * In this case, $_ROW is actually a Foo object.
	 *
	 *
	 * IMPORTANT: Please note that while all properties can/should be set up only once within the form's
	 * !IsPostBack() clause, the DataSource **MUST** be set **EVERY TIME**.  The contents of DataSource
	 * do NOT persist from postback to postback.
	 *
	 *
	 * The appearance of the datagrid control appears to be complicated, but keep in mind that it simply
	 * utlizes the cascading nature of how browsers render tables based on styles assigned to
	 * the <table>, <tr>, and <td>.  In short:
	 *     Appearance properties defined to the DataGrid, itself, show up as HTML Attributes
	 *     and CSS Styles within the <table> tag.
	 *
	 *     Appearance properties defined to a specific row's DataGridRowStyle will show up as
	 *     HTML attributes within that specific row's <tr> tag.
	 *
	 *     Appearance properties defined to a DataGridColumn will show up as HTML attributes
	 *     within that specific row's <td> tag.
	 *
	 * So, attributes will only show up if it is defined at that particular level.  So if you define a background color
	 * for a DataGridRowStyle for a particular row, but not for a DataGridColumn or for the DataGrid in general, that
	 * background style will only show up in that row.
	 *
	 * And due to the cascaiding nature of how browsers render tables, any undefined appearance property will simply
	 * inherit from the parent (where a <td>'s parent is the <tr>, and the <tr>'s parent is the <table>,
	 * and any defined appearance property will simply override whatever was defined by the parent.
	 *
	 *
	 * Sorting
	 * Whether or not a column can be sorted depends on whether or not you define a SortByCommand (and
	 * subsequently a ReverseSortByComamnd) on the DataGridColumn itself.  This SortByCommand is meant
	 * to be the SQL syntax used in an "ORDER BY" clause of a SQL query.  This fits in really well
	 * with the CodeGen Framework's Class::LoadArrayByXXX() and Class::LoadAll() which takes "$strSortInfo"
	 * as an optional parameter.
	 *
	 * If a DataGrid is being sorted by a specific column, DataGrid::SortInfo will return to you the contents
	 * of DataGridColumn::SortByCommand (or ReverseSortByCommand if it's a reverse sort) for the specific
	 * column being sorted by.  Therefore, you can set up your data source like:
	 *     $dtgDataGrid->DataSource = Foo::LoadAll($dtgDataGrid->SortInfo);
	 *
	 *
	 * Pagination
	 * Pagination can be toggled on and off with the DataGrid::Paginate flag.  When enabling pagination, you
	 * must specify how many items, TOTAL, are in the full list (DataGrid::TotalItemCount).  The DataGrid will
	 * then automatically calculate the SQL Limit information (as used in a "LIMIT" clause of a SQL query) to
	 * be used when querying a specific subset of the total list.  As with sorting, this fits really well
	 * with the CodeGen Framework's LoadArray methods which takes $strLimitInfo" as an optional parameter.
	 *
	 * Moreover, the codegen also auto-generates CountBy methods for every LoadAll/LoadArray method it generates
	 * to assist with the DataGrid::TotalItemCount property:
	 *     $dtgDataGrid->TotalItemCount = Foo::CountAll();
	 *     $dtgDataGrid->DataSource = Foo::LoadAll($dtgDataGrid->SortInfo, $dtgDataGrid->LimitInfo);
	 *
	 * Due to a bug with PHP, you cannot set a property of a property.  DataGrid's AlternateRowStyle, HeaderRowStyle and RowStyle
	 * are obviously instances of DataGridRowStyle objects which have properties in and of themselves.
	 * So unfortuantely, the following code will **NOT** work:
	 *     $dtgDataGrid->RowStyle->BackColor = "blue";
	 * Instead, you will need to do the following:
	 *     $objRowStyle = $dtgDataGrid->RowStyle;
	 *     $objRowStyle->BackColor = "blue";
	 *
	 * @package Controls
	 *
	 * @property QDataGridRowStyle $AlternateRowStyle is the DataGridRowStyle object that defines how "alternating rows" should be displayed
	 * @property string $BorderCollapse defines the BorderCollapse css style for the table
	 * @property QDataGridRowStyle $HeaderRowStyle is the DataGridRowStyle object that defines how the "header row" should be displayed (attributes that get rendred in the header row's <tr>)
	 * @property QDataGridRowStyle $FilterRowStyle
	 * @property QDataGridRowStyle $HeaderLinkStyle is the DataGridRowStyle object that defines how links, specifically, in the header row should be displayed.  Basically, anything defined here will show up as html attributes and css styles within the '<a href="">' tag of the link, itself, in the header.  Links in the header ONLY GET DISPLAYED when a column is sortable
	 * @property QDataGridRowStyle $RowStyle is the main or "default" DataGridRowStyle for the entire table.  Any overriding row style (see "OverrideRowStyle(int, DataGridRowStyle)" below) or any appearance properties set in AlternateRowStyle or HeaderRowStyle will be applied in those specific situations. Any appearance properties NOT set in ovverrides, alternate, or header will simply default to what RowStyle has defined.
	 * @property integer $CellPadding refers the the HTML CellPadding attribute of the <table>
	 * @property integer $CellSpacing refers the the HTML CellSpacing attribute of the <table>
	 * @property string $GridLines refers the the HTML rules attribute of the <table>
	 * @property boolean $ShowHeader is the flag of whether or not to show the Header row
	 * @property boolean $ShowFooter
	 * @property boolean $ShowFilter
	 * @property boolean $ShowFilterButton
	 * @property boolean $ShowFilterResetButton
	 * @property mixed $OrderByClause
	 * @property mixed $SortInfo
	 * @property-read integer $CurrentRowIndex is the current row index that is being rendered.  Useful for render-helper functions that may get called when rendering the datagrid, itself
	 * @property integer $SortColumnIndex is the current column that is being "sorted by" (or -1 if none)
	 * @property integer $SortDirection specifies the direction of that sort, 0 for SortBy, and 1 for ReverseSortBy
	 * @property string $LabelForNoneFound
	 * @property string $LabelForOneFound
	 * @property string $LabelForMultipleFound
	 * @property string $LabelForPaginated
	 * @property mixed $Owner
	 * @property mixed $FilterInfo
	 * @property mixed $Conditions
	 * @property-write QPaginatorBase $Paginator
	 * @property-write QPaginatorBase $PaginatorAlternate
	 * @property-write boolean $UseAjax
	 * @property-write string $RowActionParameterHtml
	 * @property-write mixed $Owner
	 * @property-read QButton $FilterButton
	 * @property-read QWaitIcon $WaitIcon
	 */
	abstract class QDataGridBase extends QPaginatedControl {
		// APPEARANCE
		protected $objAlternateRowStyle = null;
		protected $strBorderCollapse = QBorderCollapse::NotSet;
		protected $objHeaderRowStyle = null;
		protected $objFilterRowStyle = null;
		protected $objOverrideRowStyleArray = null;
		protected $objHeaderLinkStyle = null;
		protected $objRowStyle = null;
		protected $objWaitIcon = 'default';

		// LAYOUT
		protected $intCellPadding = -1;
		protected $intCellSpacing = -1;
		protected $strGridLines = QGridLines::None;
		protected $blnShowHeader = true;
		protected $blnShowFilter = false;
		protected $blnShowFilterButton = true;
		protected $blnShowFilterResetButton = true;
		protected $blnShowFooter = false;

		// MISC
		protected $objColumnArray;

		protected $intRowCount = 0;
		protected $intCurrentRowIndex;
		protected $intSortColumnIndex = -1;
		protected $intCurrentColumnId = 1;
		protected $intSortDirection = 0;
		
		protected $strLabelForNoneFound;
		protected $strLabelForOneFound;
		protected $strLabelForMultipleFound;
		protected $strLabelForPaginated;
		
		protected $strRowActionParameterHtml;
		protected $objRowEventArray = array();
		protected $objRowActionArray = array();

		protected $objOwner = null;

		protected $btnFilter = null;
		protected $btnFilterResetArray = array();

		public function AddRowAction($objEvent, $objAction) {
			array_push($this->objRowEventArray, $objEvent);
			array_push($this->objRowActionArray, $objAction);
		}
		
		public function __construct($objParentObject, $strControlId = null) {
			try {
				parent::__construct($objParentObject, $strControlId);
			} catch (QCallerException  $objExc) {
				$objExc->IncrementOffset();
				throw $objExc;
			}
			$this->objRowStyle = new QDataGridRowStyle();
			$this->objAlternateRowStyle = new QDataGridRowStyle();
			$this->objHeaderRowStyle = new QDataGridRowStyle();
			$this->objHeaderLinkStyle = new QDataGridRowStyle();
			$this->objFilterRowStyle = new QDataGridRowStyle();

			// Labels
			$this->strLabelForNoneFound = QApplication::Translate('<b>Results:</b> No %s found.');
			$this->strLabelForOneFound = QApplication::Translate('<b>Results:</b> 1 %s found.');
			$this->strLabelForMultipleFound = QApplication::Translate('<b>Results:</b> %s %s found.');
			$this->strLabelForPaginated = QApplication::Translate('<b>Results:</b>&nbsp;Viewing&nbsp;%s&nbsp;%s-%s&nbsp;of&nbsp;%s.');
			
			$this->objColumnArray = array();

			// Setup Sorting Events
			if ($this->blnUseAjax)
				$this->prxDatagridSorting->AddAction(new QClickEvent(), new QAjaxControlAction($this, 'Sort_Click'));
			else
				$this->prxDatagridSorting->AddAction(new QClickEvent(), new QServerControlAction($this, 'Sort_Click'));

			$this->prxDatagridSorting->AddAction(new QClickEvent(), new QTerminateAction());

			// The wait icon should only be created if the filter is shown
			if($this->blnShowFilter) {
				$this->objWaitIcon_Create();
			}
		}

		// Used to add a DataGridColumn to this DataGrid
		public function AddColumn(QDataGridColumn $objColumn) {
			$this->blnModified = true;
			$this->objColumnArray[] = $objColumn;
		}
		
		public function AddColumnAt($intColumnIndex, QDataGridColumn $objColumn) {
			$this->blnModified = true;
			try {
				$intColumnIndex = QType::Cast($intColumnIndex, QType::Integer);
			} catch (QInvalidCastException $objExc) {
				$objExc->IncrementOffset();
				throw $objExc;
			}
			if (($intColumnIndex < 0) ||
				($intColumnIndex > (count($this->objColumnArray))))
				throw new QIndexOutOfRangeException($intColumnIndex, "AddColumnAt()");

			if ($intColumnIndex == 0) {
				$this->objColumnArray = array_merge(array($objColumn), $this->objColumnArray);
			} else {
				$this->objColumnArray = array_merge(array_slice($this->objColumnArray, 0, $intColumnIndex),
					array($objColumn),
					array_slice($this->objColumnArray, $intColumnIndex));
			}
		}

		public function RemoveColumn($intColumnIndex) {
			$this->blnModified = true;
			try {
				$intColumnIndex = QType::Cast($intColumnIndex, QType::Integer);
			} catch (QInvalidCastException $objExc) {
				$objExc->IncrementOffset();
				throw $objExc;
			}
			if (($intColumnIndex < 0) ||
				($intColumnIndex > (count($this->objColumnArray) - 1)))
				throw new QIndexOutOfRangeException($intColumnIndex, "RemoveColumn()");

			array_splice($this->objColumnArray, $intColumnIndex, 1);
		}

		public function RemoveColumnByName($strName) {
			$this->blnModified = true;
			for ($intIndex = 0; $intIndex < count($this->objColumnArray); $intIndex++)
				if ($this->objColumnArray[$intIndex]->Name == $strName) {
					array_splice($this->objColumnArray, $intIndex, 1);
					return;
				}
		}

		public function RemoveColumnsByName($strName) {
			$this->blnModified = true;
			for ($intIndex = 0; $intIndex < count($this->objColumnArray); $intIndex++)
				if ($this->objColumnArray[$intIndex]->Name == $strName) {
					array_splice($this->objColumnArray, $intIndex, 1);
					$intIndex--;
				}
		}

		public function RemoveAllColumns() {
			$this->blnModified = true;
			$this->objColumnArray = array();
		}

		public function GetAllColumns() {
			return $this->objColumnArray;
		}

		public function GetColumn($intColumnIndex) {
			if (array_key_exists($intColumnIndex, $this->objColumnArray))
				return $this->objColumnArray[$intColumnIndex];
		}

		public function GetColumnByName($strName) {
			if ($this->objColumnArray) foreach ($this->objColumnArray as $objColumn)
				if ($objColumn->Name == $strName)
					return $objColumn;
		}

		public function GetColumnsByName($strName) {
			$objColumnArrayToReturn = array();
			if ($this->objColumnArray) foreach ($this->objColumnArray as $objColumn)
				if ($objColumn->Name == $strName)
					array_push($objColumnArrayToReturn, $objColumn);
			return $objColumnArrayToReturn;
		}

		// If you want to override a SPECIFIC row's style, you can specify
		// the RowIndex and the DataGridRowStyle with which to override		
		public function OverrideRowStyle($intRowIndex, $objStyle) {
			try {
				$objStyle = QType::Cast($objStyle, "QDataGridRowStyle");
			} catch (QInvalidCastException $objExc) {
				$objExc->IncrementOffset();
				throw $objExc;
			}
			$this->objOverrideRowStyleArray[$intRowIndex] = $objStyle;
		}

		// Used upon rendering to find backticks and perform PHP eval's
		protected function ParseColumnHtml($objColumn, $objObject) {
			return QDataGridBase::ParseHtml($objColumn->Html, $this, $objColumn, $objObject);
		}
		
		// Used upon rendering to find backticks and perform PHP eval's
		public static function ParseHtml($strHtml, $objControl, $objColumn, $objObject) {
			$_ITEM = $objObject;
			$_FORM = $objControl->objForm;
			$_CONTROL = $objControl;
			$_COLUMN = $objColumn;
			$_OWNER = $objControl->objOwner;

			$intPosition = 0;
			
			while (($intStartPosition = strpos($strHtml, '<?=', $intPosition)) !== false) {
				$intEndPosition = strpos($strHtml, '?>', $intStartPosition);
				if ($intEndPosition === false)
					return $strHtml;
				$strToken = substr($strHtml, $intStartPosition + 3, ($intEndPosition - $intStartPosition) - 3);
				$strToken = trim($strToken);
				
				if ($strToken) {
					// Because Eval doesn't utilize exception management, we need to do hack thru the PHP Error Handler
					set_error_handler("DataGridEvalHandleError");
					global $__exc_dtg_errstr;
					$__exc_dtg_errstr = sprintf("Incorrectly formatted DataGrid HTML in %s: %s", $objControl->strControlId, $strHtml);

					try {
						$strEvaledToken = eval(sprintf('return %s;', $strToken));
					} catch (QCallerException $objExc) {
						$objExc->DecrementOffset();
						throw $objExc;
					}

					// Restore the original error handler
					set_error_handler("QcodoHandleError");
					$__exc_dtg_errstr = null;
					unset($__exc_dtg_errstr);
				} else {
					$strEvaledToken = '';
				}
				// fix for the not so magic __toString behaviour in PHP < 5.2.0
				if (is_object($strEvaledToken)){
					if($strEvaledToken instanceof QDateTime)
						$strEvaledToken = $strEvaledToken->qFormat();
					else
						$strEvaledToken = $strEvaledToken->__toString();
				}					

				$strHtml = sprintf("%s%s%s",
					substr($strHtml, 0, $intStartPosition),
					$strEvaledToken,
					substr($strHtml, $intEndPosition + 2));

				$intPosition = $intStartPosition + strlen($strEvaledToken);
			}

			return $strHtml;
		}

		// The Table, itself, should have no actions defined on it and should not be parsing anything
		public function ParsePostData() {}

		public function GetAttributes($blnIncludeCustom = true, $blnIncludeAction = false) {
			$strToReturn = parent::GetAttributes($blnIncludeCustom, $blnIncludeAction);

			if ($this->strGridLines == QGridLines::Horizontal)
				$strToReturn .= 'rules="rows" ';
			else if ($this->strGridLines == QGridLines::Vertical)
				$strToReturn .= 'rules="cols" ';
			else if ($this->strGridLines == QGridLines::Both)
				$strToReturn .= 'rules="all" ';

			if ($this->intCellPadding >= 0)
				$strToReturn .= sprintf('cellpadding="%s" ', $this->intCellPadding);

			if ($this->intCellSpacing >= 0)
				$strToReturn .= sprintf('cellspacing="%s" ', $this->intCellSpacing);

			$strBorder = $this->strBorderWidth;
			settype($strBorder, QType::Integer);
			$strToReturn .= sprintf('border="%s" ', $strBorder);

			if ($this->strBorderColor)
				$strToReturn .= sprintf('bordercolor="%s" ', $this->strBorderColor);

			return $strToReturn;
		}
		
		public function GetStyleAttributes() {
			$strToReturn = parent::GetStyleAttributes();

			if ($this->strBorderCollapse == QBorderCollapse::Collapse) 
				$strToReturn .= 'border-collapse:collapse;';
			else if ($this->strBorderCollapse == QBorderCollapse::Separate) 
				$strToReturn .= 'border-collapse:separate;';

			return $strToReturn;
		}

		// Parse the _POST to see if the user is requesting a change in the sort column or page
		public function Sort_Click($strFormId, $strControlId, $strParameter) {
			$this->blnModified = true;

			if (strlen($strParameter)) {
				// Sorting
				$intColumnIndex = QType::Cast($strParameter, QType::Integer);
				$objColumn = $this->objColumnArray[$intColumnIndex];
				
				// First, reset pagination (if applicable)
				if ($this->objPaginator)
					$this->PageNumber = 1;

				// First, make sure the Column is Sortable
				if ($objColumn->OrderByClause) {
					// It is
					
					// Are we currently sorting by this column?
					if ($this->intSortColumnIndex == $intColumnIndex) {
						// Yes we are currently sorting by this column
						
						// In Reverse?
						if ($this->intSortDirection == 1) {
							// Yep -- unreverse the sort
							$this->intSortDirection = 0;
						} else {
							// Nope -- can we reverse?
							if ($objColumn->ReverseOrderByClause)
								$this->intSortDirection = 1;
						}
					} else {
						// Nope -- so let's set it to this column
						$this->intSortColumnIndex = $intColumnIndex;
						$this->intSortDirection = 0;
					}
				} else {
					// It isn't -- clear all sort properties
					$this->intSortDirection = 0;
					$this->intSortColumnIndex = -1;
				}
			}
		}

		protected function GetPaginatorRowHtml($objPaginator) {
			$strToReturn = "  <span class=\"right\">";
			$strToReturn .= $objPaginator->Render(false);
			$strToReturn .= "</span>\r\n  <span class=\"left\">";
			if ($this->TotalItemCount > 0) {
				$intStart = (($this->PageNumber - 1) * $this->ItemsPerPage) + 1;
				$intEnd = $intStart + count($this->DataSource) - 1;
				$strToReturn .= sprintf($this->strLabelForPaginated,
					$this->strNounPlural,
					$intStart,
					$intEnd,
					$this->TotalItemCount);
			} else {
				$intCount = count($this->objDataSource);
				if ($intCount == 0)
					$strToReturn .= sprintf($this->strLabelForNoneFound, $this->strNounPlural);
				else if ($intCount == 1)
					$strToReturn .= sprintf($this->strLabelForOneFound, $this->strNoun);
				else
					$strToReturn .= sprintf($this->strLabelForMultipleFound, $intCount, $this->strNounPlural);
			}

			$strToReturn .= "</span>\r\n";
			
			return $strToReturn;
		}
		
		protected function GetHeaderSortedHtml(QDataGridColumn $objColumn) {
			$strToReturn = sprintf('<span style="text-transform: uppercase;">%s</span>', $objColumn->Name);

			if ($this->intSortDirection == 0)
				$strToReturn .= sprintf(' <img src="%s/sort_arrow.png" width="7" height="7" border="0" alt="Sorted" />', __VIRTUAL_DIRECTORY__ . __IMAGE_ASSETS__);
			else
				$strToReturn .= sprintf(' <img src="%s/sort_arrow_reverse.png" width="7" height="7" border="0" alt="Reverse Sorted" />', __VIRTUAL_DIRECTORY__ . __IMAGE_ASSETS__);

			return $strToReturn;
		}

		protected function GetHeaderRowHtml() {
			$objHeaderStyle = $this->objRowStyle->ApplyOverride($this->objHeaderRowStyle);

			$strToReturn = sprintf("  <tr %s>\r\n", $objHeaderStyle->GetAttributes());
			$intColumnIndex = 0;
			if ($this->objColumnArray) foreach ($this->objColumnArray as $objColumn) {
				if ($objColumn->OrderByClause) {						
					// This Column is Sortable
					if ($intColumnIndex == $this->intSortColumnIndex)
						$strName = $this->GetHeaderSortedHtml($objColumn);
					else
						$strName = $objColumn->Name;

					$this->strActionParameter = $intColumnIndex;

					$strToReturn .= sprintf("    <th %s><a id=\"%s\" href=\"%s\" %s%s>%s</a></th>\r\n",
						$this->objHeaderRowStyle->GetAttributes(),
						$this->ControlId . "_col_" . $this->strActionParameter,
                        			QApplication::$RequestUri,
						$this->prxDatagridSorting->RenderAsEvents($this->strActionParameter, true, $this->ControlId . "_col_" . $this->strActionParameter, false),
						$this->objHeaderLinkStyle->GetAttributes(),
						$strName);
				} else
					$strToReturn .= sprintf("    <th %s>%s</th>\r\n", $this->objHeaderRowStyle->GetAttributes(), $objColumn->Name);
				$intColumnIndex++;
			}
			$strToReturn .= "  </tr>\r\n";

			return $strToReturn;
		}
		
		protected function GetDataGridRowHtml($objObject) {
			// Get the Default Style
			$objStyle = $this->objRowStyle;

			// Iterate through the Columns
			$strColumnsHtml = '';
			foreach ($this->objColumnArray as $objColumn) {
				try {
					$strHtml = $this->ParseColumnHtml($objColumn, $objObject);

					if ($objColumn->HtmlEntities)
						$strHtml = QApplication::HtmlEntities($strHtml);

					 // For IE
					if (QApplication::IsBrowser(QBrowserType::InternetExplorer) &&
						($strHtml == ''))
							$strHtml = '&nbsp;';
				} catch (QCallerException $objExc) {
					$objExc->IncrementOffset();
					throw $objExc;
				}
				$strColumnsHtml .= sprintf("    <td %s>%s</td>\r\n", $objColumn->GetAttributes(), $strHtml);
			}

			// Apply AlternateRowStyle (if applicable)
			if (($this->intCurrentRowIndex % 2) == 1)
				$objStyle = $objStyle->ApplyOverride($this->objAlternateRowStyle);

			// Apply any Style Override (if applicable)
			if ((is_array($this->objOverrideRowStyleArray)) && 
				(array_key_exists($this->intCurrentRowIndex, $this->objOverrideRowStyleArray)) &&
				(!is_null($this->objOverrideRowStyleArray[$this->intCurrentRowIndex])))
				$objStyle = $objStyle->ApplyOverride($this->objOverrideRowStyleArray[$this->intCurrentRowIndex]);

			// Make the event string
			$strTrId = sprintf("%srow%s", $this->strControlId, $this->intCurrentRowIndex);
			$numEvents = count($this->objRowEventArray);
			if ($numEvents > 0) {
				$objRow = $this->objForm->GetControl($strTrId);
				if (!$objRow) {
					$objRow = new QDataGridRow($this->objForm, $strTrId);
					// add all the row actions to this proxy 
					for ($i = 0; $i < $numEvents; ++$i) {
						$objRow->AddAction($this->objRowEventArray[$i], $this->objRowActionArray[$i]);
					}
				}
				$objRow->Style = $objStyle;
				// parse the action parameter
				$objRow->ActionParameter = QDataGridBase::ParseHtml($this->strRowActionParameterHtml, $this, null, $objObject);
				$strToReturn = $objRow->GetHtml($strColumnsHtml);
				QApplication::ExecuteJavaScript($objRow->GetActionAttributes());
			} else {
				// If there are no events, don't create any row controls'
				// Finish up
				$strToReturn = sprintf('<tr id="%s" %s>%s</tr>', $strTrId, $objStyle->GetAttributes(), $strColumnsHtml);
			}
			$this->intCurrentRowIndex++;
			return $strToReturn;
		}

		protected function GetFooterRowHtml() {}

		protected function GetControlHtml() {
			$this->DataBind();

			// Table Tag
			$strStyle = $this->GetStyleAttributes();
			if ($strStyle)
				$strStyle = sprintf('style="%s" ', $strStyle);
			$strToReturn = sprintf("<table id=\"%s\" %s%s>\r\n", $this->strControlId, $this->GetAttributes(), $strStyle);

			// Paginator Row (if applicable)
			if ($this->objPaginator)
				$strToReturn .= "<caption>\r\n" . $this->GetPaginatorRowHtml($this->objPaginator) . "</caption>\r\n";

			// Header Row (if applicable)
			if ($this->blnShowHeader)
			{
				$strToReturn .= "<thead>\r\n" . $this->GetHeaderRowHtml();

				// Filter Row (if applicable)
				if ($this->blnShowFilter)
					$strToReturn .= $this->GetFilterRowHtml();

				$strToReturn .=  "</thead>\r\n";
			}

			// Footer Row (if applicable)
			if ($this->blnShowFooter)
				$strToReturn .= "<tfoot>\r\n" . $this->GetFooterRowHtml() . "</tfoot>\r\n";

			// DataGrid Rows
			$strToReturn .= "<tbody>\r\n";
			$this->intCurrentRowIndex = 0;
			if ($this->objDataSource)
				foreach ($this->objDataSource as $objObject)
					$strToReturn .= $this->GetDataGridRowHtml($objObject);
			// Cleanup all the extra rows from the previous rendering
			for ($i = $this->intCurrentRowIndex; $i < $this->intRowCount; ++$i) {
				$strTrId = sprintf("%srow%s", $this->strControlId, $i);
				$this->objForm->RemoveControl($strTrId);
			}
			$this->intRowCount = $this->intCurrentRowIndex;

			$strToReturn .= "</tbody>\r\n";

			// Finish Up
			$strToReturn .= '</table>';
			$this->objDataSource = null;
			return $strToReturn;
		}

		protected function PersistPrepare() {
			parent::PersistPrepare();
			$this->RemoveAllColumns();
		}

		/******
		* Create the row used for datagrid filtering
		* @return string $strToReturn of html table row
		*/
		protected function GetFilterRowHtml() {
			$objFilterStyle = $this->objRowStyle->ApplyOverride($this->objFilterRowStyle);
			$strToReturn = sprintf('  <tr %s>'."\r\n", $objFilterStyle->GetAttributes());
			$intColumnIndex = 0;
			if($this->objColumnArray !== null)
			{
				$blnResetButtonRendered = false;
				for ($intIndex = 0; $intIndex < count($this->objColumnArray); $intIndex++)
				{
					$objColumn = $this->objColumnArray[$intIndex];

					$colContent = '&nbsp;';

					if ($objColumn->Filter !== null || 
						$objColumn->FilterByCommand !== null || 
						$objColumn->FilterType != QFilterType::None)
					{
						// This Column is Filterable
						$ctlFilter = $this->GetFilterControl($objColumn);

						if(null !== $ctlFilter)
							//display the control
							$colContent = $ctlFilter->Render(false);
					}
					if ($this->ShowFilterResetButton) {
						if (!$blnResetButtonRendered && $intIndex == count($this->objColumnArray) -1) {
							// no column has the reset button, but ShowFilterResetButton is true
							// put the reset button in the last column
							$objColumn->HasResetButton = true;
						}
						$btnReset = $this->GetResetButton($objColumn);
						if (null !== $btnReset) {
							$colContent .= $btnReset->Render(false);
							$blnResetButtonRendered = true;
						}
					}

					//show the filter button in the last column (if set to)
					if($intIndex == count($this->objColumnArray) -1)
					{
						$btnFilter = $this->FilterButton;
						if (null !== $btnFilter) {
							$colContent .= $btnFilter->Render(false);
						}
						$colContent .= $this->objWaitIcon->Render(false);
					}

					$strToReturn .= sprintf('    <th %s>%s</th>'."\r\n",
							$this->objFilterRowStyle->GetAttributes(),
							$colContent);
				}
			}
			$strToReturn .= '  </tr>'."\r\n";
			return $strToReturn;
		}

		protected function GetColumnFilterControlId($objColumn) {
			if ($objColumn->FilterColId === null)
				$objColumn->FilterColId = $this->intCurrentColumnId++;
			return 'ctl'.$this->ControlId.'flt'.$objColumn->FilterColId;
		}

		public function GetFilterControl($objColumn)
		{
			$strControlId = $this->GetColumnFilterControlId($objColumn);
			//find/build the control
			if(($ctlFilter = $this->GetChildControl($strControlId)) === null)
				//create the control this first time
				$ctlFilter = $this->CreateFilterControl($strControlId, $objColumn);
			return $ctlFilter;
		}

		public function GetResetButton($objColumn)
		{
			if (!$this->ShowFilterResetButton)
				return null;
			if (!$objColumn->HasResetButton)
				return null;

			$strControlId = $this->GetColumnFilterControlId($objColumn);
			if (!array_key_exists($strControlId, $this->btnFilterResetArray)) {
				return ($this->btnFilterResetArray[$strControlId] = $this->btnFilterReset_Create());
			}
			return $this->btnFilterResetArray[$strControlId];
		}

		/******
		* CreateControls used in the filter row and set their fiter values if available. 
		* @param string $strControlId id based on the column that the control is contained
		* @param QDataGridColumn $objColumn the QDataGridColumn that contains the filter data. 
		* @return QControl $control the input control used for filtering
		*/
		//this, btnReset_Click and GetControlValue are the functions to override/change if you want to add new types

		protected function CreateFilterControl($strControlId, $objColumn)
		{
			#region Find the value
			//show the current filter in the control
			$value = null;
			//for manual queries
			if (isset($objColumn->FilterByCommand['value']))
				$value = $objColumn->FilterByCommand['value'];
			//for lists
			elseif (null !==$objColumn->Filter && $objColumn->FilterType == QFilterType::ListFilter)
				$value = array_search($objColumn->Filter,$objColumn->FilterList);
			//or for text
			elseif(null !== $objColumn->FilterList && count($objColumn->FilterList) > 0 && $objColumn->FilterType == QFilterType::TextFilter)
			{
				$value = $objColumn->FilterList[0]->mixOperand;
				if(null !== $value)
				{
					//Strip prefix and postfix
					if(null !== $objColumn->FilterPrefix)
					{
						$prefixLength = strlen($objColumn->FilterPrefix);
						if(substr($value, 0, $prefixLength) == $objColumn->FilterPrefix)
							$value = substr($value, $prefixLength);
					}
					if(null !== $objColumn->FilterPostfix)
					{
						$postfixLength = strlen($objColumn->FilterPostfix);
						if(substr($value, strlen($value) - $postfixLength) == $objColumn->FilterPostfix)
							$value = substr($value, 0, strlen($value) - $postfixLength);
					}
				}
			}
			#endregion

			#region Create the control
			//create the appropriate kind of control
			$actionName = 'btnFilter_Click';
			$ctlFilter = null;
			switch($objColumn->FilterType)
			{
				default:
				case QFilterType::TextFilter:
					$ctlFilter = $this->filterTextBox_Create($strControlId, $objColumn->Name, $objColumn->FilterBoxSize, $value);
					break;
				case QFilterType::ListFilter:
					$ctlFilter = $this->filterListBox_Create($strControlId, $objColumn->Name, $objColumn->FilterList, $value);
					break;
			}
			#endregion

			if(null !== $ctlFilter)
			{
				//make sure hitting enter applies the filter
				if ($this->blnUseAjax)
					$ctlFilter->AddAction(new QEnterKeyEvent(), new QAjaxControlAction($this, $actionName, $this->objWaitIcon));
				else
					$ctlFilter->AddAction(new QEnterKeyEvent(), new QServerControlAction($this, $actionName));

				$ctlFilter->AddAction(new QEnterKeyEvent(), new QTerminateAction());
			}

			return $ctlFilter;
		}

		/******
		* Get the control's filter input for filtering
		* @param string $type id based on the column that the control is contained
		* @param obj $control the filter control to get the filter input 
		* @return string The input used for filtering
		*/
		//this, btnReset_Click and CreateControl are the functions to override/change if you want to add new types
		protected function GetFilterControlValue($strFilterType, $ctlControl) {
			//depending on the control, the members used to store the value are different
			$strValue = null;
			switch($strFilterType) {
				default:
				case QFilterType::TextFilter:
					$strValue = $ctlControl->Text;
					if($strValue == '')
						$strValue = null;
					break;
				case QFilterType::ListFilter:
					$strValue = $ctlControl->SelectedValue;
					break;
			}
			return $strValue;
		}

		/**
		 * This function creates a textbox suitable for the filter bar
		* @param string $strControlId id based on the column that the control is contained
		* @param string $strControlName The name to give the textbox
		* @param int $columns The Columns setting to use for the textbox
		* @param string $strValue The text to fill the textbox with
		* @return QTextBox the resulting textbox
		**/
		protected function filterTextBox_Create($strControlId, $strControlName, $columns, $strValue) {
			$ctlFilterTextBox = new QTextBox($this, $strControlId);
			$ctlFilterTextBox->Name = $strControlName;
			$ctlFilterTextBox->Text = QType::Cast($strValue, QType::String);
			$ctlFilterTextBox->FontSize = $this->RowStyle->FontSize;
			$ctlFilterTextBox->Columns = $columns;

			return $ctlFilterTextBox;
		}

		/**
		* This function creates a listbox suitable for the filter bar
		* @param string $strControlId id based on the column that the control is contained
		* @param string $strControlName The name to give the textbox
		* @param string[] $arrListValues A name=>value array of items to add to the list
		* @param string $strSelectedValue The value to start selected
		* @return QListBox the resulting listbox
		**/
		protected function filterListBox_Create($strControlId, $strControlName, $arrListValues, $strSelectedValue)
		{
			$ctlFilterListbox = new QListBox($this, $strControlId);
			$ctlFilterListbox->Name = $strControlName;
			$ctlFilterListbox->AddItem('-'.QApplication::Translate('Any').'-');
			$ctlFilterListbox->FontSize = $this->RowStyle->FontSize;
			$ctlFilterListbox->Width = 'auto';

			//Now fill up the advanced list
			foreach (array_keys($arrListValues) as $strFilterName) {
				$ctlFilterListbox->AddItem($strFilterName,$strFilterName);
			}
			$ctlFilterListbox->SelectedName = $strSelectedValue;
			return $ctlFilterListbox;
		}


		/**
		 * Creates the reset button for the filter row
		 *
		 * @return void 
		 *
		 */
		protected function btnFilterReset_Create() {
			$btnFilterReset = new QButton($this);
			$btnFilterReset->Text = QApplication::Translate('Reset');;

			if ($this->blnUseAjax)
				$btnFilterReset->AddAction(new QClickEvent(), new QAjaxControlAction($this, 'btnFilterReset_Click', $this->objWaitIcon));
			else
				$btnFilterReset->AddAction(new QClickEvent(), new QServerControlAction($this, 'btnFilterReset_Click'));
			$btnFilterReset->AddAction(new QClickEvent(), new QTerminateAction());
			return $btnFilterReset;
		}


		/**
		 * Creates the Filter button for the filter row
		 *
		 * @return void 
		 *
		 */
		protected function btnFilter_Create()
		{
			$btnFilter = new QButton($this);
			$btnFilter->Name = QApplication::Translate('Filter');
			$btnFilter->Text = QApplication::Translate('Filter');

			if ($this->blnUseAjax)
				$btnFilter->AddAction(new QClickEvent(), new QAjaxControlAction($this, 'btnFilter_Click', $this->objWaitIcon));
			else
				$btnFilter->AddAction(new QClickEvent(), new QServerControlAction($this, 'btnFilter_Click'));

			$btnFilter->AddAction(new QClickEvent(), new QTerminateAction());

			$btnFilter->CausesValidation = false;
			return $btnFilter;
		}


		/**
		 * Creates the objWaitIcon for this datagrid, since we don't want to use the default form one
		 *
		 * @return void 
		 *
		 */
		protected function objWaitIcon_Create()
		{
			$this->objWaitIcon = new QWaitIcon($this);
		}

		/******
		* For each column, get its input filter value and set the columns filter with it.
		* @param string $strFormId
		* @param string $strControlId
		* @param string $strParameter
		*/
			public function btnFilter_Click($strFormId, $strControlId, $strParameter) 
		{
			//set the filter commands
			foreach($this->objColumnArray as $objColumn)
			{
				if ($objColumn->FilterByCommand !== null || $objColumn->FilterType != QFilterType::None)
				{
					//a filter is defined for this column
					$ctlFilter = $this->GetChildControl($this->GetColumnFilterControlId($objColumn));
					if($ctlFilter !== null)
					{
						//we've found the control that has it's value
						$strValue = $this->GetFilterControlValue($objColumn->FilterType, $ctlFilter);

						//deal with any manual filters
						if ($objColumn->FilterByCommand !== null)
						{
							//update the column's filterByCommand with the user-entered value
							$filter = $objColumn->FilterByCommand;

							if($strValue !== null)
								$filter['value'] = $strValue;
							else if(isset($filter['value']))
								unset($filter['value']);

							$objColumn->FilterByCommand = $filter;
						}
						//Handle the other methods differently
						elseif($strValue !== null)
						{
							switch($objColumn->FilterType) {
								case QFilterType::ListFilter:
									$objColumn->FilterActivate($strValue);
									break;
								default:
								case QFilterType::TextFilter;
									$objColumn->FilterActivate();
									$objColumn->FilterSetOperand($strValue);
									break;
							}
						}
						else
							$objColumn->ClearFilter();
					}
				}
			}
			//reset to page 1
			if ($this->objPaginator)
				$this->PageNumber = 1;

			$this->DataBind();

			$this->MarkAsModified();
		}

		/******
		* Clear all  filter column control input values.
		* @param $strFormId = null, $strControlId = null, $strParameter = null
		*/
		public function ClearFilters()
		{
			foreach($this->objColumnArray as $objColumn)
			{
				if($objColumn->FilterByCommand !== null || $objColumn->FilterType != QFilterType::None)
				{
					//both modes clear in the same way
					$ctlFilter = $this->GetChildControl($this->GetColumnFilterControlId($objColumn));
					if ($ctlFilter !== null)
					{
						switch($objColumn->FilterType)
						{
							default:
							case QFilterType::TextFilter:
								$ctlFilter->Text = '';
								break;
							case QFilterType::ListFilter:
								$ctlFilter->SelectedIndex = 0;
								break;
						}
						$objColumn->ClearFilter();
					}
				}
			}

			//reset to page 1
			if ($this->objPaginator)
				$this->PageNumber = 1;
			$this->MarkAsModified();
		}

		/******
		* Click handler for the reset button
		* @param $strFormId = null, $strControlId = null, $strParameter = null
		*/
		//this, GetControlValue and CreateControl are the functions to override/change if you want to add new types
		public function btnFilterReset_Click($strFormId, $strControlId, $strParameter) 
		{
			$this->ClearFilters();
		}

		/******
		* Set Filter values (used to restore a previously saved state)
		* @param array $filters array of filters indexed by column name
		* contain either a string or a filter object
			*/
		public function SetFilters($filters)
		{
			foreach($this->objColumnArray as $col)
			{
				if(isset($filters[$col->Name]))
				{
					if(null !== $col->FilterByCommand)
					{
						//if filterbycommand is used
						$filterCommand = $col->FilterByCommand;
						$filterCommand['value'] = $filters[$col->Name];
						$col->FilterByCommand = $filterCommand;
					}
					//AddListItem with filters dont enter this check until filter button clicked
					elseif($col->FilterType == QFilterType::TextFilter && 
						$col->FilterList !== null && count($col->FilterList) == 1) {
						if($col->FilterList[0] instanceof QQConditionComparison)
						{
							$col->Filter = $filters[$col->Name];
							$col->FilterActivate();
						}
					}
					elseif ($col->FilterType == QFilterType::ListFilter){
						$col->Filter = $filters[$col->Name];
					}
				}
			}
		}
		/******
		* Get Filter values from each column (used to save a state)
		* @return array $filters array of filters indexed by column name
		*/
		public function GetFilters()
		{
			$filters = array();
			foreach($this->objColumnArray as $col)
			{
				if(isset($col->FilterByCommand['value']))
				{
					//manual filter
					$filterCommand = $col->FilterByCommand;
					$filters[$col->Name] = $filterCommand['value'];
				}
				elseif($col->Filter !== null) 
				{
					if($col->Filter instanceof QQConditionComparison)
					{
						$filters[$col->Name] = $col->Filter;
					}
					else
						throw new exception(QApplication::Translate("Unknown Filter type"));
				}
			}
			return $filters;
		}

		/////////////////////////
		// Public Properties: GET
		/////////////////////////
		public function __get($strName) {
			switch ($strName) {
				// APPEARANCE
				case "AlternateRowStyle": return $this->objAlternateRowStyle;
				case "BorderCollapse": return $this->strBorderCollapse;
				case "HeaderRowStyle": return $this->objHeaderRowStyle;
				case "FilterRowStyle": return $this->objFilterRowStyle;
				case "HeaderLinkStyle": return $this->objHeaderLinkStyle;
				case "RowStyle": return $this->objRowStyle;

				// LAYOUT
				case "CellPadding": return $this->intCellPadding;
				case "CellSpacing": return $this->intCellSpacing;
				case "GridLines": return $this->strGridLines;
				case "ShowHeader": return $this->blnShowHeader;
				case "ShowFooter": return $this->blnShowFooter;
				case "ShowFilter": return $this->blnShowFilter;
				case "ShowFilterButton": return $this->blnShowFilterButton;
				case 'ShowFilterResetButton': return $this->blnShowFilterResetButton;

				// MISC
				case "OrderByClause":
					if ($this->intSortColumnIndex >= 0) {
						if ($this->intSortDirection == 0)
							return $this->objColumnArray[$this->intSortColumnIndex]->OrderByClause;
						else
							return $this->objColumnArray[$this->intSortColumnIndex]->ReverseOrderByClause;
					} else
						return null;
				case "SortInfo":
					if ($this->intSortColumnIndex >= 0) {
						if ($this->intSortDirection == 0) {
							$mixToReturn = $this->objColumnArray[$this->intSortColumnIndex]->SortByCommand;
							if ($mixToReturn instanceof QQOrderBy)
								return $mixToReturn->GetAsManualSql();
							else
								return $mixToReturn;
						} else {
							$mixToReturn = $this->objColumnArray[$this->intSortColumnIndex]->ReverseSortByCommand;
							if ($mixToReturn instanceof QQOrderBy)
								return $mixToReturn->GetAsManualSql();
							else
								return $mixToReturn;
						}
					} else
						return null;

				case "CurrentRowIndex": return $this->intCurrentRowIndex;
				case "SortColumnIndex": return $this->intSortColumnIndex;
				case "SortDirection": return $this->intSortDirection;

				case 'LabelForNoneFound': return $this->strLabelForNoneFound;
				case 'LabelForOneFound': return $this->strLabelForOneFound;
				case 'LabelForMultipleFound': return $this->strLabelForMultipleFound;
				case 'LabelForPaginated': return $this->strLabelForPaginated;
				case 'Owner' : return $this->objOwner;

				case "FilterInfo":
					$filterArray = array();
					foreach($this->objColumnArray as $col)
					{
						if(isset($col->FilterByCommand['value']))
						{
							//manual filter
							$filterCommand = $col->FilterByCommand;
							$filterCommand['clause_operator'] = 'AND';
							//apply the pre and postfix
							$filterCommand['value'] = $filterCommand['prefix'] . $filterCommand['value'] . $filterCommand['postfix'];
							$filterArray[] = $filterCommand;
						}
					}
					return $filterArray;
				case "Conditions":
					//Calculate the conditions to apply to the entire grid based on the column's filters
					$dtgConditions = QQ::All();
					foreach($this->objColumnArray as $objColumn)
					{
							$colCondition = null;
							//if there's a normal filter type, return the QQConditions related to it
							if ($objColumn->FilterType != QFilterType::None) {
								$colCondition = null;
								if ($objColumn->Filter !== null)
									$colCondition = $objColumn->Filter;
							}

							/*FilterConstant allows us to specify a custom QQuery that applies in addition to any 
							user-specified filters. EG: If the user enters a Cost to filter on, also filter on 
							object actually being sold*/
							if(null !== $colCondition && null !== $objColumn->FilterConstant)
								$colCondition = QQ::AndCondition($colCondition, $objColumn->FilterConstant);

							//now after all the above checks if the column has a condition to be specified
							//we add it to overall conditions. but if the column conditions are null we leave
							//overall conditions as is
							if($colCondition !== null) {
								//if there are no overall conditions yet change them to reflect the column condition
								if($dtgConditions == QQ::All())
									$dtgConditions = $colCondition;
								else
									//combine the overall conditions with the column conditions
									$dtgConditions = QQ::AndCondition($dtgConditions, $colCondition);
							}
					}

					return $dtgConditions;

				case "FilterButton":
					if (!$this->ShowFilterButton)
						return null;
					if (null !== $this->btnFilter)
						return $this->btnFilter;
					return ($this->btnFilter = $this->btnFilter_Create());

				case "WaitIcon": return $this->objWaitIcon;

				default:
					try {
						return parent::__get($strName);
					} catch (QCallerException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
			}
		}


		/////////////////////////
		// Public Properties: SET
		/////////////////////////
		public function __set($strName, $mixValue) {
			switch ($strName) {
				// APPEARANCE
				case "AlternateRowStyle":
					try {
						$this->objAlternateRowStyle = QType::Cast($mixValue, "QDataGridRowStyle");
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case "BorderCollapse":
					try {
						$this->strBorderCollapse = QType::Cast($mixValue, QType::String);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case "HeaderRowStyle":
					try {
						$this->objHeaderRowStyle = QType::Cast($mixValue, "QDataGridRowStyle");
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case "HeaderLinkStyle":
					try {
						$this->objHeaderLinkStyle = QType::Cast($mixValue, "QDataGridRowStyle");
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case "FilterRowStyle":
					try {
						$this->objFilterRowStyle = QType::Cast($mixValue, "QDataGridRowStyle");
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case "RowStyle":
					try {
						$this->objRowStyle = QType::Cast($mixValue, "QDataGridRowStyle");
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

				// BEHAVIOR
				case "Paginator":
					//do whatever needs done
					try {
						$blnToReturn = parent::__set($strName, $mixValue);
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

					//Now make sure it knows about our spinner
					$this->objPaginator->WaitIcon = $this->objWaitIcon;
					return $blnToReturn;
					break;

				case "PaginatorAlternate":
					//do whatever needs done
					try {
						$blnToReturn = parent::__set($strName, $mixValue);
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

					//Now make sure it knows about our spinner
					$this->objPaginatorAlternate->WaitIcon = $this->objWaitIcon;
					return $blnToReturn;
					break;
				case "UseAjax":
					try {
						$blnToReturn = parent::__set($strName, $mixValue);
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

					// Because we are switching to/from Ajax, we need to reset the events
					$this->prxDatagridSorting->RemoveAllActions(QClickEvent::EventName);
					if ($this->blnUseAjax)
						$this->prxDatagridSorting->AddAction(new QClickEvent(), new QAjaxControlAction($this, 'Sort_Click', $this->objWaitIcon));
					else
						$this->prxDatagridSorting->AddAction(new QClickEvent(), new QServerControlAction($this, 'Sort_Click'));

					$this->prxDatagridSorting->AddAction(new QClickEvent(), new QTerminateAction());

					$actionName = 'btnFilter_Click';
					foreach($this->objColumnArray as $objColumn) 
					{
						if ($objColumn->FilterByCommand !== null || $objColumn->FilterType != QFilterType::None)
						{
							$ctlFilter = $this->GetChildControl($this->GetColumnFilterControlId($objColumn));
							if ($ctlFilter !== null) 
							{
								$ctlFilter->RemoveAllActions(QKeyDownEvent::EventName);
								if ($this->blnUseAjax)
									$ctlFilter->AddAction(new QEnterKeyEvent(), new QAjaxControlAction($this, $actionName, $this->objWaitIcon));
								else
									$ctlFilter->AddAction(new QEnterKeyEvent(), new QServerControlAction($this, $actionName));

								$ctlFilter->AddAction(new QEnterKeyEvent(), new QTerminateAction());
							}
						}
					}

					if (null !== $this->btnFilter) {
						$this->btnFilter->RemoveAllActions(QClickEvent::EventName);
						if ($this->blnUseAjax)
							$this->btnFilter->AddAction(new QClickEvent(), new QAjaxControlAction($this, $actionName, $this->objWaitIcon));
						else
							$this->btnFilter->AddAction(new QClickEvent(), new QServerControlAction($this, $actionName));
						$this->btnFilter->AddAction(new QClickEvent(), new QTerminateAction());
					}

					foreach ($this->btnFilterResetArray as $btnFilterReset) {
						$btnFilterReset->RemoveAllActions(QClickEvent::EventName);
						if ($this->blnUseAjax)
							$btnFilterReset->AddAction(new QClickEvent(), new QAjaxControlAction($this, 'btnFilterReset_Click', $this->objWaitIcon));
						else
							$btnFilterReset->AddAction(new QClickEvent(), new QServerControlAction($this, 'btnFilterReset_Click'));
						$btnFilterReset->AddAction(new QClickEvent(), new QTerminateAction());
					}
					return $blnToReturn;

				// LAYOUT
				case "CellPadding":
					try {
						$this->intCellPadding = QType::Cast($mixValue, QType::Integer);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case "CellSpacing":
					try {
						$this->intCellSpacing = QType::Cast($mixValue, QType::Integer);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case "GridLines":
					try {
						$this->strGridLines = QType::Cast($mixValue, QType::String);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case "ShowHeader":
					try {
						$this->blnShowHeader = QType::Cast($mixValue, QType::Boolean);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

				case "ShowFooter":
					try {
						$this->blnShowFooter = QType::Cast($mixValue, QType::Boolean);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case "ShowFilter":
					try {
						$this->blnShowFilter = QType::Cast($mixValue, QType::Boolean);
						// If the filter is being shown, the table gets its own wait icon. 
						// When the wait icon changes we need to refresh a lot of actions
						// that would otherwise use the default wait icon.
						if($this->blnShowFilter){
							$this->objWaitIcon_Create();
						} else{
							$this->objWaitIcon = "default";
						}
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case "ShowFilterButton":
					try {
						$this->blnShowFilterButton = QType::Cast($mixValue, QType::Boolean);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case 'ShowFilterResetButton':
					try {
						$this->blnShowFilterResetButton = QType::Cast($mixValue, QType::Boolean);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				// MISC
				case "SortColumnIndex":
					try {
						$this->intSortColumnIndex = QType::Cast($mixValue, QType::Integer);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

				case "SortDirection":
					if ($mixValue == 1)
						$this->intSortDirection = 1;
					else
						$this->intSortDirection = 0;
					break;


				case 'LabelForNoneFound':
					try {
						$this->strLabelForNoneFound = QType::Cast($mixValue, QType::String);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

				case 'LabelForOneFound':
					try {
						$this->strLabelForOneFound = QType::Cast($mixValue, QType::String);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

				case 'LabelForMultipleFound':
					try {
						$this->strLabelForMultipleFound = QType::Cast($mixValue, QType::String);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

				case 'LabelForPaginated':
					try {
						$this->strLabelForPaginated = QType::Cast($mixValue, QType::String);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

				case 'RowActionParameterHtml':
					try {
						$this->strRowActionParameterHtml = QType::Cast($mixValue, QType::String);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

				case 'Owner':
					try {
						$this->objOwner = $mixValue;
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

				default:
					try {
						parent::__set($strName, $mixValue);
						break;
					} catch (QCallerException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
			}
		}
	}
?>
